import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.FileInputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;

import NBT.NBT_Tag;
import NBT.TAG_Byte_Array;
import NBT.TAG_Compound;
import NBT.TAG_Int;
import NBT.MapChunk;

public class MinecraftMap {
	
	File path;
	List<MapChunk> chunks;
	int spawnX, spawnZ;
	
	int minx,maxx,minz,maxz;
	
	MinecraftMap(File path) throws Exception
	{
		if(!path.isDirectory()) throw new Exception("Path is not a directory!");
		
		this.path=path;
		chunks=new LinkedList<MapChunk>();
	}

	void loadData() throws Exception
	{
		loadAllChunks();
		getSpawnPoint();		
		
		minx=10000;
		minz=10000;
		maxx=-10000;
		maxz=-10000;
		
		Iterator<MapChunk> iter=chunks.iterator();
		while(iter.hasNext())
		{
			MapChunk chunk=iter.next();
			if(chunk.xPos>maxx) maxx=chunk.xPos;
			if(chunk.xPos<minx) minx=chunk.xPos;
			if(chunk.zPos>maxz) maxz=chunk.zPos;
			if(chunk.zPos<minz) minz=chunk.zPos;
		}

	}
	
	BufferedImage render()
	{	
		BufferedImage ret=null;
		
		int width=(maxx-minx+1)*16;
		int height=(maxz-minz+1)*16;
		
		int red=Color.red.getRGB();
		int green=Color.green.getRGB();
		int blue=Color.blue.getRGB();
		int gray=Color.gray.getRGB();
		int black=Color.black.getRGB();
		int yellow=Color.yellow.getRGB();
		int brown=new Color(165,83,0).getRGB();
		int white=Color.white.getRGB();
		
		ret=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g=ret.createGraphics();
		g.setColor(Color.white);
		g.fillRect(0, 0, width, height);
		
		
		int count=0;
		//Main.progressReset(chunks.size());
		Iterator<MapChunk>iter=chunks.iterator();
		while(iter.hasNext())
		{
			//Main.progressAdvance(count);
			count++;
			MapChunk chunk=iter.next();
			byte[] blocks=chunk.blocks;
			byte v;
			int alt;
			int b=0,col;
			int offsetx=16*(chunk.xPos-minx);
			int offsetz=16*(chunk.zPos-minz);
			for(int x=0; x<16; x++)
				for(int z=0; z<16; z++)
				{
					v=0;
					alt=127;
					for(int y=127; y>=0; y--)
					//for(int y=14; y>=13; y--)
						if(blocks[b+y]!=0)
						{
							v=blocks[b+y];
							alt=y;
							break;
						}
					
					b+=128;					
					
					switch(v)
					{
					case 1: //stone
					case 4: //cobblestone
					case 7: //bedrock
					case 13://gravel
						col=gray;
						break;
					case 8: //water
					case 9: //stationary water
					case 79: //ice
						col=blue;
						break;
					case 2: //grass
					case 6: //sapling
					case 18://leaves
						col=green;
						break;
					case 3: //dirt
						col=brown;
						break;
					case 12://sand
						col=yellow;
						break;
					case 78://snow
					case 80://snow block
						col=white;
						break;
					default:
						col=black;
					}
					
					int cr=(col&0xff0000)>>16,cg=(col&0xff00)>>8,cb=col&0xff;
					double m=(double)alt/127.0;
					//double m=1;
					cr=(int)((double)cr*m);
					cg=(int)((double)cg*m);
					cb=(int)((double)cb*m);
					col=cb+(cg<<8)+(cr<<16);
										
					if(offsetx+x>=0 && offsetx+x<width && offsetz+z>=0 && offsetz+z<height)
						ret.setRGB(offsetx+x, offsetz+z, col);
					
				}
			//Main.refresh(ret, false);
						
		}
		
		int x=spawnX-minx*16;
		int z=spawnZ-minz*16;
		
		if(x>=0 && z>=0 && x<width && z<height )
		{
			g.setColor(Color.red);			
			g.drawLine(x-3,z-3,x+3,z+3);
			g.drawLine(x-3,z+3,x+3,z-3);
		}
		
		return ret;
	}
	
	void loadAllChunks()
	{
		chunks.clear();
		
		File [] xlist=path.listFiles();
		
		if(xlist==null)
			Log.say("Path isn't a directory!");
		
		//Main.progressReset(xlist.length);
		for(int i=0; i<xlist.length; i++)
		{
			//Main.progressAdvance(i);
			if(!xlist[i].isDirectory()) continue;
			
			String name=xlist[i].getName();
			
			try{
				int x=Integer.parseInt(name,36);
				if(x<0 || x>=64) continue;
			}
			catch(Exception e){ continue; }
			
			File [] zlist=xlist[i].listFiles();
			for(int j=0; j<zlist.length; j++)
			{
				if(!zlist[j].isDirectory()) continue;
				
				String name2=zlist[j].getName();
				
				try{
					int x=Integer.parseInt(name2,36);
					if(x<0 || x>=64) continue;
				}
				catch(Exception e){ continue; }
			
				File [] chunkfiles=zlist[j].listFiles();
				
				for(int k=0; k<chunkfiles.length; k++)
				{
					String chunkname=chunkfiles[k].getName();
					
					if(!chunkname.matches("c\\.-?[0-9a-z][0-9a-z]?.-?[0-9a-z][0-9a-z]?.dat")) continue;
					try{
						chunks.add(new MapChunk(chunkfiles[k]));
					}catch(Exception e)
					{
						Log.say("Cannot read file "+chunkfiles[k].getAbsolutePath()+": "+e);
					}
				}
			}
		}
		//Main.progressAdvance(xlist.length);
	}
	
	void clearChunks()
	{
		minx=10000;
		minz=10000;
		maxx=-10000;
		maxz=-10000;
		
		chunks.clear();
	}
	
	boolean loadChunk(int x_pos, int z_pos)
	{
		
		MapChunk chunk;
		try{
			chunk=MapChunk.load(path, x_pos, z_pos);
			chunks.add(chunk);
		}catch(Exception e)
		{
			Log.say("Cannot read chunk: "+e);
			return false;
		}
		
		if(chunk.xPos>maxx) maxx=chunk.xPos;
		if(chunk.xPos<minx)	minx=chunk.xPos;
		if(chunk.zPos>maxz) maxz=chunk.zPos;
		if(chunk.zPos<minz) minz=chunk.zPos;		
		
		return true;
	}
	
	MapChunk getChunk(int x_pos, int z_pos)
	{
		try{
			return MapChunk.load(path, x_pos, z_pos);
		}catch(Exception e)
		{
			Log.say("Cannot read chunk: "+e);		
		}
		
		return null;
	}
	
	File getChunkPath(int x_pos, int z_pos) throws Exception
	{
		return MapChunk.getPath(path, x_pos, z_pos);
	}
	
	void getSpawnPoint() throws Exception
	{
		File file=new File(path.getAbsolutePath()+"/level.dat");
		
		try{			
			GZIPInputStream stream=new GZIPInputStream(new FileInputStream(file));
			
			TAG_Compound root=(TAG_Compound) NBT_Tag.make(stream);
			TAG_Compound data=(TAG_Compound) root.getElement("Data");
			
			TAG_Int x=(TAG_Int) data.getElement("SpawnX");
			TAG_Int z=(TAG_Int) data.getElement("SpawnZ");
			
			spawnX=x.value;
			spawnZ=z.value;
			
			stream.close();
			
		}catch(Exception e)
		{
			throw e;
		}
	}
}